import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

/**
 * Работа с тестами в файловой системе. Структура папки теста - файл tests.txt, results.txt, testxx.txt
 * В файле tests.txt - названия и названия файлов с входными данными
 * В файле results.txt - названия и результаты тестов
 * Файлы testxx.txt - входные файлы тестов.
 * 
 * @author Arkanif
 */
public class RemoteAutotesterTest {

	/**
	 * Человекопонятное название теста
	 */
	private String testName;
	/**
	 * Название файла с входными данными теста
	 */
	private String testInputFileName;
	/**
	 * Полный путь к файлу с входными данными теста
	 */
	private String testInputFileFullPath;
	/**
	 * Выходные данные теста
	 */
	private ArrayList<String> testOutput;

	/**
	 * Конструктор теста
	 *
	 * @param testName Человекопонятное название теста
	 */
	public RemoteAutotesterTest(String testName) {
		this(testName, null, null);
	}

	/**
	 * Конструктор теста
	 *
	 * @param testName Человекопонятное название теста
	 * @param testInputFileName Название файла с входными данными теста
	 */
	public RemoteAutotesterTest(String testName, String testInputFileName) {
		this(testName, testInputFileName, null);
	}

	/**
	 * Конструктор теста
	 *
	 * @param testName Человекопонятное название теста
	 * @param testInputFileName Название файла с входными данными теста
	 * @param testOutput Выходные данные теста
	 */
	public RemoteAutotesterTest(String testName, String testInputFileName, ArrayList<String> testOutput) {
		this.testName = testName;
		this.testInputFileName = testInputFileName;
		this.testOutput = testOutput;
	}

	/**
	 * Получить название теста
	 * 
	 * @return название теста
	 */
	public String getTestName() {
		return this.getTestName(false);
	}

	/**
	 * Получить название теста
	 *
	 * @param padded дополнять ли нулями номер теста до трех цифр
	 * @return название теста
	 */
	public String getTestName(boolean padded) {
		if (padded) {
			int index = testName.indexOf('.');
			if (index == 2)
				return "0" + testName;
			if (index == 1)
				return "00" + testName;
		}
		return testName;
	}

	/**
	 * Получить название файла с входными данными теста
	 * 
	 * @return название файла с входными данными теста
	 */
	public String getTestInputFileName() {
		return testInputFileName;
	}

	/**
	 * Получить выходные данные теста
	 * 
	 * @return выходные данные теста
	 */
	public ArrayList<String> getTestOutput() {
		return testOutput;
	}


	/**
	 * Получить полный путь к входному файлу теста
	 *
	 * @return полный путь к входному файлу теста
	 */
	public String getTestInputFileFullPath() {
		return testInputFileFullPath;
	}

	/**
	 * Задать полный путь к входному файлу теста
	 * @param testInputFileFullPath полный путь к входному файлу теста
	 */
	public void setTestInputFileFullPath(String testInputFileFullPath) {
		this.testInputFileFullPath = testInputFileFullPath;
	}

	/**
	 * Задать название файла с входными данными теста
	 * @param testInputFileName Название файла с входными данными теста
	 */
	public void setTestInputFileName(String testInputFileName) {
		this.testInputFileName = testInputFileName;
	}

	/**
	 * Задать выходные данные теста
	 * 
	 * @param testOutput Выходные данные теста
	 */
	public void setTestOutput(ArrayList<String> testOutput) {
		this.testOutput = testOutput;
	}

	/**
	 * Преобразование теста в строку
	 * 
	 * @return Преобразованный в строку тест
	 */
	@Override
	public String toString() {
		return "<" + this.testName + ", " + this.testInputFileName + ", " + this.testOutput + ">";
	}

	/**
	 * Получить список тестов из каталога на сервере.
	 *
	 * @param dirPath Папка с тестовыми файлами
	 * @return Массив тестов
	 */
	public static ArrayList<RemoteAutotesterTest> getTestsFromDir(String dirPath) {
		ArrayList<RemoteAutotesterTest> tests = parseTestNames(dirPath);
		return parseTestResults(dirPath, tests);
	}

	/**
	 * Разбор файла, содержащего описание тестов и их файлы с входными данными.
	 *
	 * @param dirPath Папка с тестовыми файлами
	 * @return Массив тестов
	 */
	private static ArrayList<RemoteAutotesterTest> parseTestNames(String dirPath) {
		ArrayList<RemoteAutotesterTest> tests = new ArrayList<>();
		ArrayList<String> list = FileUtils.readFile(dirPath + "tests.txt");

		Pattern p = Pattern.compile("^test\\d\\d\\.txt");
		Iterator it = list.iterator();
		while (it.hasNext()) {
			String line = (String) it.next();
			if (!p.matcher(line).matches()) {
				String testName = line;
				if (it.hasNext()) {
					line = (String) it.next();
					if (line != null && p.matcher(line).matches()) {
						RemoteAutotesterTest test = new RemoteAutotesterTest(testName, line);
						test.setTestInputFileFullPath(dirPath + line);
						tests.add(test);
					}
				}

			}
		}
		return tests;
	}

	/**
	 * Разбор файлов, содержащих результаты тестов.
	 * 
	 * @param dirPath Папка с тестовыми файлами
	 * @param tests Массив тестов
	 * @return Массив тестов, дополненный выходными данными тестов
	 */
	private static ArrayList<RemoteAutotesterTest> parseTestResults(String dirPath, ArrayList<RemoteAutotesterTest> tests) {
		ArrayList<String> fileContent = FileUtils.readFile(dirPath + "result.txt", "Cp1251");

		for (int i = 0; i < tests.size(); i++) {
			boolean lastTest = (i == tests.size() - 1);
			for (int j = 0; j < fileContent.size(); j++) {
				if (fileContent.get(j).equals(tests.get(i).getTestName())) {
					// Если встретилась строка, совпадающая с названием одного из тестов, начать запись выходных данных
					ArrayList<String> output = new ArrayList<>();
					for (int k = j + 1; k < fileContent.size(); k++) {
						if (lastTest || !fileContent.get(k).equals(tests.get(i + 1).getTestName())) {
							if (fileContent.get(k).length() > 0) {
								output.add(fileContent.get(k));
							}
						} else {
							if (!lastTest && fileContent.get(k).equals(tests.get(i + 1).getTestName())) {
								// Если есть следующий тест и его название встретилось, прекратить запись выходных данных
								break;
							}
						}
					}
					tests.get(i).setTestOutput(output);
				}
			}
		}
		return tests;
	}
}
